home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / Utilities / stevie 3.10 / ops.c < prev    next >
Text File  |  1991-01-03  |  12KB  |  651 lines

  1. /* $Header: /nw/tony/src/stevie/src/RCS/ops.c,v 1.5 89/08/06 09:50:42 tony Exp $
  2.  *
  3.  * Contains routines that implement the operators in vi. Everything in this
  4.  * file is called only from code in normal.c
  5.  */
  6.  
  7. #include "stevie.h"
  8. #include "ops.h"
  9.  
  10. /*
  11.  * doshift - handle a shift operation
  12.  */
  13. void
  14. doshift(op, c1, c2, num)
  15. int    op;
  16. char    c1, c2;
  17. int    num;
  18. {
  19.     void    tabinout();
  20.     LPTR    top, bot;
  21.     int    nlines;
  22.     char    opchar;
  23.  
  24.     top = startop;
  25.     bot = *Curschar;
  26.  
  27.     if (lt(&bot, &top))
  28.         pswap(&top, &bot);
  29.  
  30.     u_save(top.linep->prev, bot.linep->next);
  31.  
  32.     nlines = cntllines(&top, &bot);
  33.     *Curschar = top;
  34.     tabinout((op == LSHIFT), nlines);
  35.  
  36.     /* construct Redo buff */
  37.     opchar = (op == LSHIFT) ? '<' : '>';
  38.     if (num != 0)
  39.         sprintf(Redobuff, "%c%d%c%c", opchar, num, c1, c2);
  40.     else
  41.         sprintf(Redobuff, "%c%c%c", opchar, c1, c2);
  42.  
  43.     /*
  44.      * The cursor position afterward is the prior of the two positions.
  45.      */
  46.     *Curschar = top;
  47.  
  48.     /*
  49.      * If we were on the last char of a line that got shifted left,
  50.      * then move left one so we aren't beyond the end of the line
  51.      */
  52.     if (gchar(Curschar) == NUL && Curschar->index > 0)
  53.         Curschar->index--;
  54.  
  55.     updatescreen();
  56.  
  57.     if (nlines > P(P_RP))
  58.         smsg("%d lines %ced", nlines, opchar);
  59. }
  60.  
  61. /*
  62.  * dodelete - handle a delete operation
  63.  */
  64. void
  65. dodelete(c1, c2, num)
  66. char    c1, c2;
  67. int    num;
  68. {
  69.     LPTR    top, bot;
  70.     int    nlines;
  71.     register int    n;
  72.  
  73.     /*
  74.      * Do a yank of whatever we're about to delete. If there's too much
  75.      * stuff to fit in the yank buffer, then get a confirmation before
  76.      * doing the delete. This is crude, but simple. And it avoids doing
  77.      * a delete of something we can't put back if we want.
  78.      */
  79.     if (!doyank()) {
  80.         msg("yank buffer exceeded: press <y> to confirm");
  81.         if (vgetc() != 'y') {
  82.             msg("delete aborted");
  83.             *Curschar = startop;
  84.             return;
  85.         }
  86.     }
  87.  
  88.     top = startop;
  89.     bot = *Curschar;
  90.  
  91.     if (lt(&bot, &top))
  92.         pswap(&top, &bot);
  93.  
  94.     u_save(top.linep->prev, bot.linep->next);
  95.  
  96.     nlines = cntllines(&top, &bot);
  97.     *Curschar = top;
  98.     cursupdate();
  99.  
  100.     if (mtype == MLINE) {
  101.         delline(nlines, TRUE);
  102.     } else {
  103.         if (!mincl && bot.index != 0)
  104.             dec(&bot);
  105.  
  106.         if (top.linep == bot.linep) {        /* del. within line */
  107.             n = bot.index - top.index + 1;
  108.             while (n--)
  109.                 if (!delchar(TRUE))
  110.                     break;
  111.         } else {                /* del. between lines */
  112.             n = Curschar->index;
  113.             while (Curschar->index >= n)
  114.                 if (!delchar(TRUE))
  115.                     break;
  116.  
  117.             top = *Curschar;
  118.             *Curschar = *nextline(Curschar);
  119.             delline(nlines-2, TRUE);
  120.             Curschar->index = 0;
  121.             n = bot.index + 1;
  122.             while (n--)
  123.                 if (!delchar(TRUE))
  124.                     break;
  125.             *Curschar = top;
  126.             (void) dojoin(FALSE);
  127.             oneright();    /* we got bumped left up above */
  128.         }
  129.     }
  130.  
  131.     /* construct Redo buff */
  132.     if (num != 0)
  133.         sprintf(Redobuff, "d%d%c%c", num, c1, c2);
  134.     else
  135.         sprintf(Redobuff, "d%c%c", c1, c2);
  136.  
  137.     if (mtype == MCHAR && nlines == 1)
  138.         updateline();
  139.     else
  140.         updatescreen();
  141.  
  142.     if (nlines > P(P_RP))
  143.         smsg("%d fewer lines", nlines);
  144. }
  145.  
  146. /*
  147.  * dofilter - handle a filter operation
  148.  */
  149.  
  150. #define    ITMP    "viXXXXXX"
  151. #define    OTMP    "voXXXXXX"
  152.  
  153. static    char    itmp[32];
  154. static    char    otmp[32];
  155.  
  156.  
  157. /*
  158.  * dofilter - filter lines through a command given by the user
  159.  *
  160.  * We use temp files and the system() routine here. This would normally
  161.  * be done using pipes on a UNIX machine, but this is more portable to
  162.  * the machines we usually run on. The system() routine needs to be able
  163.  * to deal with redirection somehow, and should handle things like looking
  164.  * at the PATH env. variable, and adding reasonable extensions to the
  165.  * command name given by the user. All reasonable versions of system()
  166.  * do this.
  167.  */
  168. void
  169. dofilter(c1, c2, num)
  170. char    c1, c2;
  171. int    num;
  172. {
  173. #ifndef MACINTOSH
  174.     char    *mktemp();
  175.     static    char    *lastcmd = NULL;/* the last thing we did */
  176.     char    *buff;            /* cmd buffer from getcmdln() */
  177.     char    cmdln[200];        /* filtering command line */
  178.     LPTR    top, bot;
  179.     int    nlines;
  180.  
  181.     top = startop;
  182.     bot = *Curschar;
  183.  
  184.     buff = getcmdln('!');
  185.  
  186.     if (buff == NULL)    /* user backed out of the command prompt */
  187.         return;
  188.  
  189.     if (*buff == '!') {        /* use the 'last' command */
  190.         if (lastcmd == NULL) {
  191.             emsg("No previous command");
  192.             return;
  193.         }
  194.         buff = lastcmd;
  195.     }
  196.  
  197.     /*
  198.      * Remember the current command
  199.      */
  200.     if (lastcmd != NULL)
  201.         free(lastcmd);
  202.     lastcmd = strsave(buff);
  203.  
  204.     if (lt(&bot, &top))
  205.         pswap(&top, &bot);
  206.  
  207.     u_save(top.linep->prev, bot.linep->next);
  208.  
  209.     nlines = cntllines(&top, &bot);
  210.     *Curschar = top;
  211.     cursupdate();
  212.  
  213.     /*
  214.      * 1. Form temp file names
  215.      * 2. Write the lines to a temp file
  216.      * 3. Run the filter command on the temp file
  217.      * 4. Read the output of the command into the buffer
  218.      * 5. Delete the original lines to be filtered
  219.      * 6. Remove the temp files
  220.      */
  221.  
  222. #ifdef    TMPDIR
  223.     strcpy(itmp, TMPDIR);
  224.     strcpy(otmp, TMPDIR);
  225. #else
  226.     itmp[0] = otmp[0] = NUL;
  227. #endif
  228.     strcat(itmp, ITMP);
  229.     strcat(otmp, OTMP);
  230.  
  231.     if (mktemp(itmp) == NULL || mktemp(otmp) == NULL) {
  232.         emsg("Can't get temp file names");
  233.         return;
  234.     }
  235.  
  236.     if (!writeit(itmp, &top, &bot)) {
  237.         emsg("Can't create input temp file");
  238.         return;
  239.     }
  240.  
  241.     sprintf(cmdln, "%s <%s >%s", buff, itmp, otmp);
  242.  
  243.     if (system(cmdln) != 0) {
  244.         emsg("Filter command failed");
  245.         remove(ITMP);
  246.         return;
  247.     }
  248.  
  249.     if (readfile(otmp, &bot, TRUE)) {
  250.         emsg("Can't read filter output");
  251.         return;
  252.     }
  253.  
  254.     delline(nlines, TRUE);
  255.  
  256.     remove(itmp);
  257.     remove(otmp);
  258.  
  259.     /* construct Redo buff */
  260.     if (num != 0)
  261.         sprintf(Redobuff, "d%d%c%c", num, c1, c2);
  262.     else
  263.         sprintf(Redobuff, "d%c%c", c1, c2);
  264.  
  265.     updatescreen();
  266.  
  267.     if (nlines > P(P_RP))
  268.         smsg("%d lines filtered", nlines);
  269. #endif
  270. }
  271.  
  272. #ifdef    TILDEOP
  273. void
  274. dotilde(c1, c2, num)
  275. char    c1, c2;
  276. int    num;
  277. {
  278.     LPTR    top, bot;
  279.     register char    c;
  280.  
  281.     /* construct Redo buff */
  282.     if (num != 0)
  283.         sprintf(Redobuff, "~%d%c%c", num, c1, c2);
  284.     else
  285.         sprintf(Redobuff, "~%c%c", c1, c2);
  286.  
  287.     top = startop;
  288.     bot = *Curschar;
  289.  
  290.     if (lt(&bot, &top))
  291.         pswap(&top, &bot);
  292.  
  293.     u_save(top.linep->prev, bot.linep->next);
  294.  
  295.     if (mtype == MLINE) {
  296.         top.index = 0;
  297.         bot.index = strlen(bot.linep->s);
  298.     } else {
  299.         if (!mincl) {
  300.             if (bot.index)
  301.                 bot.index--;
  302.         }
  303.     }
  304.  
  305.     for (; ltoreq(&top, &bot) ;inc(&top)) {
  306.         /*
  307.          * Swap case through the range
  308.          */
  309.         c = gchar(&top);
  310.         if (isalpha(c)) {
  311.             if (islower(c))
  312.                 c = toupper(c);
  313.             else
  314.                 c = tolower(c);
  315.  
  316.             pchar(&top, c);        /* Change current character. */
  317.             CHANGED;
  318.         }
  319.     }
  320.     *Curschar = startop;
  321.     updatescreen();
  322. }
  323. #endif
  324.  
  325. /*
  326.  * dochange - handle a change operation
  327.  */
  328. void
  329. dochange(c1, c2, num)
  330. char    c1, c2;
  331. int    num;
  332. {
  333.     char    sbuf[16];
  334.     bool_t    doappend;    /* true if we should do append, not insert */
  335.     bool_t    at_eof;        /* changing through the end of file */
  336.     LPTR    top, bot;
  337.  
  338.     top = startop;
  339.     bot = *Curschar;
  340.  
  341.     if (lt(&bot, &top))
  342.         pswap(&top, &bot);
  343.  
  344.     doappend = endofline(&bot);
  345.     at_eof = (bot.linep->next == Fileend->linep);
  346.  
  347.     dodelete(c1, c2, num);
  348.  
  349.     if (mtype == MLINE) {
  350.         /*
  351.          * If we made a change through the last line of the file,
  352.          * then the cursor got backed up, and we need to open a
  353.          * new line forward, otherwise we go backward.
  354.          */
  355.         if (at_eof)
  356.             opencmd(FORWARD, FALSE);
  357.         else
  358.             opencmd(BACKWARD, FALSE);
  359.     } else {
  360.         if (doappend && !lineempty())
  361.             inc(Curschar);
  362.     }
  363.  
  364.     if (num)
  365.         sprintf(sbuf, "c%d%c%c", num, c1, c2);
  366.     else
  367.         sprintf(sbuf, "c%c%c", c1, c2);
  368.  
  369.     startinsert(sbuf, mtype == MLINE);
  370. }
  371.  
  372. #ifndef    YBSIZE
  373. #define    YBSIZE    4096
  374. #endif
  375.  
  376. static    char    ybuf[YBSIZE];
  377. static    int    ybtype = MBAD;
  378.  
  379. bool_t
  380. doyank()
  381. {
  382.     LPTR    top, bot;
  383.     char    *yptr = ybuf;
  384.     char    *ybend = &ybuf[YBSIZE-1];
  385.     int    nlines;
  386.  
  387.     top = startop;
  388.     bot = *Curschar;
  389.  
  390.     if (lt(&bot, &top))
  391.         pswap(&top, &bot);
  392.  
  393.     nlines = cntllines(&top, &bot);
  394.  
  395.     ybtype = mtype;            /* set the yank buffer type */
  396.  
  397.     if (mtype == MLINE) {
  398.         top.index = 0;
  399.         bot.index = strlen(bot.linep->s);
  400.         /*
  401.          * The following statement checks for the special case of
  402.          * yanking a blank line at the beginning of the file. If
  403.          * not handled right, we yank an extra char (a newline).
  404.          */
  405.         if (dec(&bot) == -1) {
  406.             ybuf[0] = NUL;
  407.             if (operator == YANK)
  408.                 *Curschar = startop;
  409.             return TRUE;
  410.         }
  411.     } else {
  412.         if (!mincl) {
  413.             if (bot.index)
  414.                 bot.index--;
  415.         }
  416.     }
  417.  
  418.     for (; ltoreq(&top, &bot) ;inc(&top)) {
  419.         *yptr = (gchar(&top) != NUL) ? gchar(&top) : NL;
  420.         if (++yptr >= ybend) {
  421.             msg("yank too big for buffer");
  422.             ybtype = MBAD;
  423.             return FALSE;
  424.         }
  425.     }
  426.  
  427.     *yptr = NUL;
  428.  
  429.     if (operator == YANK) {    /* restore Curschar if really doing yank */
  430.         *Curschar = startop;
  431.  
  432.         if (nlines > P(P_RP))
  433.             smsg("%d lines yanked", nlines);
  434.     }
  435.  
  436.     return TRUE;
  437. }
  438.  
  439. /*
  440.  * doput(dir)
  441.  *
  442.  * Put the yank buffer at the current location, using the direction given
  443.  * by 'dir'.
  444.  */
  445. void
  446. doput(dir)
  447. int    dir;
  448. {
  449.     void    inslines();
  450.  
  451.     if (ybtype == MBAD) {
  452.         beep();
  453.         return;
  454.     }
  455.     
  456.     u_saveline();
  457.  
  458.     if (ybtype == MLINE)
  459.         inslines(Curschar->linep, dir, ybuf);
  460.     else {
  461.         /*
  462.          * If we did a character-oriented yank, and the buffer
  463.          * contains multiple lines, the situation is more complex.
  464.          * For the moment, we punt, and pretend the user did a
  465.          * line-oriented yank. This doesn't actually happen that
  466.          * often.
  467.          */
  468.         if (strchr(ybuf, NL) != NULL)
  469.             inslines(Curschar->linep, dir, ybuf);
  470.         else {
  471.             char    *s;
  472.             int    len;
  473.  
  474.             len = strlen(Curschar->linep->s) + strlen(ybuf) + 1;
  475.             s = alloc((unsigned) len);
  476.             strcpy(s, Curschar->linep->s);
  477.             if (dir == FORWARD)
  478.                 Curschar->index++;
  479.             strcpy(s + Curschar->index, ybuf);
  480.             strcat(s, &Curschar->linep->s[Curschar->index]);
  481.             free(Curschar->linep->s);
  482.             Curschar->linep->s = s;
  483.             Curschar->linep->size = len;
  484.             updateline();
  485.         }
  486.     }
  487.  
  488.     CHANGED;
  489. }
  490.  
  491. bool_t
  492. dojoin(join_cmd)
  493. bool_t    join_cmd;        /* handling a real "join" command? */
  494. {
  495.     int    scol;        /* save cursor column */
  496.     int    size;        /* size of the joined line */
  497.  
  498.     if (nextline(Curschar) == NULL)        /* on last line */
  499.         return FALSE;
  500.  
  501.     if (!canincrease(size = strlen(Curschar->linep->next->s)))
  502.         return FALSE;
  503.  
  504.     while (oneright())            /* to end of line */
  505.         ;
  506.  
  507.     strcat(Curschar->linep->s, Curschar->linep->next->s);
  508.  
  509.     /*
  510.      * Delete the following line. To do this we move the cursor
  511.      * there briefly, and then move it back. Don't back up if the
  512.      * delete made us the last line.
  513.      */
  514.     Curschar->linep = Curschar->linep->next;
  515.     scol = Curschar->index;
  516.  
  517.     if (nextline(Curschar) != NULL) {
  518.         delline(1, TRUE);
  519.         Curschar->linep = Curschar->linep->prev;
  520.     } else
  521.         delline(1, TRUE);
  522.  
  523.     Curschar->index = scol;
  524.  
  525.     if (join_cmd)
  526.         oneright();    /* go to first char. of joined line */
  527.  
  528.     if (join_cmd && size != 0) {
  529.         /*
  530.          * Delete leading white space on the joined line
  531.          * and insert a single space.
  532.          */
  533.         while (gchar(Curschar) == ' ' || gchar(Curschar) == TAB)
  534.             delchar(TRUE);
  535.         inschar(' ');
  536.     }
  537.  
  538.     return TRUE;
  539. }
  540.  
  541. void
  542. startinsert(initstr, startln)
  543. char    *initstr;
  544. int    startln;    /* if set, insert point really at start of line */
  545. {
  546.     register char    *p, c;
  547.  
  548.     *Insstart = *Curschar;
  549.     if (startln)
  550.         Insstart->index = 0;
  551.     Ninsert = 0;
  552.     Insptr = Insbuff;
  553.     for (p=initstr; (c=(*p++))!='\0'; )
  554.         *Insptr++ = c;
  555.  
  556.     if (*initstr == 'R')
  557.         State = REPLACE;
  558.     else
  559.         State = INSERT;
  560.  
  561.     if (P(P_MO))
  562.         msg((State == INSERT) ? "Insert Mode" : "Replace Mode");
  563. }
  564. /*
  565.  * tabinout(inout,num)
  566.  *
  567.  * If inout==0, add a tab to the begining of the next num lines.
  568.  * If inout==1, delete a tab from the beginning of the next num lines.
  569.  */
  570. static void
  571. tabinout(inout, num)
  572. int    inout;
  573. int    num;
  574. {
  575.     int    ntodo = num;
  576.     LPTR    *p;
  577.  
  578.     beginline(FALSE);
  579.     while (ntodo-- > 0) {
  580.         beginline(FALSE);
  581.         if (inout == 0)
  582.             inschar(TAB);
  583.         else {
  584.             if (gchar(Curschar) == TAB)
  585.                 delchar(TRUE);
  586.         }
  587.         if ( ntodo > 0 ) {
  588.             if ((p = nextline(Curschar)) != NULL)
  589.                 *Curschar = *p;
  590.             else
  591.                 break;
  592.         }
  593.     }
  594. }
  595.  
  596. /*
  597.  * inslines(lp, dir, buf)
  598.  *
  599.  * Inserts lines in the file from the given buffer. Lines are inserted
  600.  * before or after "lp" according to the given direction flag. Newlines
  601.  * in the buffer result in multiple lines being inserted. The cursor
  602.  * is left on the first of the inserted lines.
  603.  */
  604. static void
  605. inslines(lp, dir, buf)
  606. LINE    *lp;
  607. int    dir;
  608. char    *buf;
  609. {
  610.     register char    *cp = buf;
  611.     register int    len;
  612.     char    *ep;
  613.     LINE    *l, *nc = NULL;
  614.  
  615.     if (dir == BACKWARD)
  616.         lp = lp->prev;
  617.  
  618.     do {
  619.         if ((ep = strchr(cp, NL)) == NULL)
  620.             len = strlen(cp);
  621.         else
  622.             len = ep - cp;
  623.  
  624.         l = newline(len);
  625.         if (len != 0)
  626.             strncpy(l->s, cp, len);
  627.         l->s[len] = NUL;
  628.  
  629.         l->next = lp->next;
  630.         l->prev = lp;
  631.         lp->next->prev = l;
  632.         lp->next = l;
  633.  
  634.         if (nc == NULL)
  635.             nc = l;
  636.  
  637.         lp = lp->next;
  638.  
  639.         cp = ep + 1;
  640.     } while (ep != NULL);
  641.  
  642.     if (dir == BACKWARD)    /* fix the top line in case we were there */
  643.         Filemem->linep = Filetop->linep->next;
  644.  
  645.     renum();
  646.  
  647.     updatescreen();
  648.     Curschar->linep = nc;
  649.     Curschar->index = 0;
  650. }
  651.